package at.jku.cp.mir;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.IOUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class LastFMArtistTagsRetriever
{

	public static void main(String[] args) throws Exception
	{
		LastFMArtistTagsRetriever artistTagsRetriever = new LastFMArtistTagsRetriever();

		artistTagsRetriever.retrieveArtistTagsFromFile("C224a.txt");
		// artistTagsRetriever.retrieveArtistTags("Georg Friedrich Händel");

		// artistTagsRetriever.retrieveTopTrackTagsFromFile("C224a.txt");
		// artistTagsRetriever.retrieveTopTrackTags("Yehudi Menuhin");
	}

	private final String API_KEY = "8ec7335b6962c3d765c2e651a43dbc41";

	private String assemblyGetArtistTopTagsUrl(String artist)
	{
		try
		{
			return "http://ws.audioscrobbler.com/2.0/?method=artist.gettoptags&artist="
					+ URLEncoder.encode(artist, "UTF-8") + "&api_key=" + API_KEY;
		} catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException("stupid system, which doesn't know UTF-8!");
		}
	}

	private String assemblyGetArtistTopTracksUrl(String artist)
	{
		try
		{
			return "http://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist="
					+ URLEncoder.encode(artist, "UTF-8") + "&api_key=" + API_KEY;
		} catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException("stupid system, which doesn't know UTF-8!");
		}
	}

	private String assemblyGetTrackTopTags(String artist, String trackName)
	{
		try
		{
			return "http://ws.audioscrobbler.com/2.0/?method=track.getTopTags&artist="
					+ URLEncoder.encode(artist, "UTF-8") + "&track=" + URLEncoder.encode(trackName, "UTF-8")
					+ "&api_key=" + API_KEY;
		} catch (UnsupportedEncodingException e)
		{
			throw new RuntimeException("stupid system, which doesn't know UTF-8!");
		}
	}

	private byte[] downloadUrl(String urlString)
	{
		InputStream in = null;
		ByteArrayOutputStream out = null;
		try
		{
			URL url = new URL(urlString);
			in = url.openStream();

			out = new ByteArrayOutputStream();
			IOUtils.copy(in, out);

			return out.toByteArray();
		} catch (Exception e)
		{
			e.printStackTrace();
		} finally
		{
			IOUtils.closeQuietly(in);
			IOUtils.closeQuietly(out);
		}
		return null;
	}

	public List<Tag> retrieveArtistTags(String currentArtist) throws FileNotFoundException
	{

		// System.out.println("--- " + currentArtist);

		// fetch the artist tags from Last.fm
		InputStream inStream = null;
		OutputStream outStream = null;
		try
		{
			String encodedArtistName = currentArtist.replace("/", "_");
			String artistFileName = "artistTags2/" + encodedArtistName + ".xml";
			File artistFile = new File(artistFileName);
			if (!artistFile.exists())
			{
				String artistUrl = assemblyGetArtistTopTagsUrl(currentArtist);
				System.out.println(artistUrl);

				URL url = new URL(artistUrl);
				inStream = url.openStream();

				outStream = new FileOutputStream(artistFile);

				IOUtils.copy(inStream, outStream);
				System.out.println("fetched artist tags");

				// no error in getting the tags, so we wait 10 seconds
				try
				{
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}

			// parse the created XML file
			SAXBuilder saxBuilder = new SAXBuilder();
			Document xmlDoc = saxBuilder.build(new File(artistFileName));
			return extractTags(xmlDoc);

		} catch (MalformedURLException e)
		{
			String errMsg = "malformed URL for artist: " + currentArtist;

			System.out.println(errMsg);
			e.printStackTrace();

		} catch (IOException e)
		{
			String errMsg = "exception while opening stream for artist: " + currentArtist;

			System.out.println(errMsg);
			e.printStackTrace();

		} catch (JDOMException e)
		{
			e.printStackTrace();
		} finally
		{
			if (inStream != null)
				IOUtils.closeQuietly(inStream);

			if (outStream != null)
				IOUtils.closeQuietly(outStream);
		}

		return null;
	}

	private List<Tag> extractTags(Document doc) throws JDOMException
	{
		List<Element> tagElements = XPath.selectNodes(doc, "//tag");
		List<Tag> tagList = new ArrayList<Tag>(tagElements.size());
		for (Element tagElement : tagElements)
		{
			String tagName = tagElement.getChildText("name");
			String countStr = tagElement.getChildText("count");
			int count = Integer.parseInt(countStr);

			tagList.add(new Tag(tagName, count));
		}

		return tagList;
	}

	public void retrieveArtistTagsFromFile(String fileName) throws FileNotFoundException
	{
		// get the file and iterate over each line
		JSONArray allArtistTags = new JSONArray();
		Scanner lineIterator = new Scanner(new File(fileName));

		Map<String, List<Tag>> artistTagMap = new TreeMap<String, List<Tag>>();
		while (lineIterator.hasNextLine())
		{
			String currentLine = lineIterator.nextLine();
			String originalArtist = currentLine;
			String currentArtist = currentLine;
			if (currentLine.contains(" --> "))
			{
				String[] artists = currentLine.split(" --> ");
				currentArtist = artists[1];
				originalArtist = artists[0];
			}
			List<Tag> tagsForThisArtist = retrieveArtistTags(currentArtist);
			if (tagsForThisArtist != null)
			{
				JSONObject artist = new JSONObject();
				artist.put("artist", originalArtist);
				artist.put("lastFMArtist", currentArtist);

				JSONArray arr = new JSONArray();
				artist.put("tags", arr);
				for (Tag tag : tagsForThisArtist)
				{
					JSONObject tagObj = new JSONObject();
					tagObj.put("name", tag.getName());
					tagObj.put("count", tag.getCount());
					arr.add(tagObj);
				}
				// System.out.println(artist);
				allArtistTags.add(artist);
			}

			artistTagMap.put(currentArtist, tagsForThisArtist);
		}

		// normalize the tags
		TagNormalizer tagNormalizer = new TagNormalizer();
		tagNormalizer.normalizeTags(artistTagMap);

		try
		{
			IOUtils.copy(new StringReader(allArtistTags.toJSONString()), new FileOutputStream("step1.json"));
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	public void retrieveTopTrackTags(String artist) throws Exception
	{
		String encodedArtistName = artist.replace("/", "_");
		String artistFileName = "artistTags3/" + encodedArtistName;
		File artistFileDir = new File(artistFileName);
		if (artistFileDir.exists() && false)
			return;

		// create the dir
		artistFileDir.mkdirs();

		// get the top tracks XML document
		String topTracksUrl = assemblyGetArtistTopTracksUrl(artist);
		ByteArrayOutputStream outputStream = null;
		try
		{
			URL url = new URL(topTracksUrl);
			InputStream inStream = url.openStream();

			outputStream = new ByteArrayOutputStream();
			IOUtils.copy(inStream, outputStream);

		} catch (MalformedURLException e)
		{
			e.printStackTrace();
			return;
		} catch (IOException e)
		{
			e.printStackTrace();
			return;
		}

		SAXBuilder builder = new SAXBuilder();
		XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
		Document topTracksDoc = builder.build(new ByteArrayInputStream(outputStream.toByteArray()));

		List<Element> trackNames = XPath.selectNodes(topTracksDoc, "//track/name");
		int end = Math.min(10, trackNames.size());
		trackNames = trackNames.subList(0, end);
		int count = 0;
		for (Element trackNameElement : trackNames)
		{
			String trackName = trackNameElement.getTextTrim();
			String trackTopTagsUrl = assemblyGetTrackTopTags(artist, trackName);
			byte[] content = downloadUrl(trackTopTagsUrl);
			Document trackTagskDoc = builder.build(new ByteArrayInputStream(content));

			// save it in folder
			File trackFile = new File(artistFileDir, "track_" + count + ".xml");
			outputter.output(trackTagskDoc, new FileOutputStream(trackFile));

			System.out.println(artist + " / track " + count);
			count++;
		}

		Thread.sleep(10000);
	}

	public void retrieveTopTrackTagsFromFile(String fileName) throws FileNotFoundException
	{
		Scanner lineIterator = new Scanner(new File(fileName));
		while (lineIterator.hasNextLine())
		{
			String currentLine = lineIterator.nextLine();
			String originalArtist = currentLine;
			String currentArtist = currentLine;
			if (currentLine.contains(" --> "))
			{
				String[] artists = currentLine.split(" --> ");
				originalArtist = artists[0];
				currentArtist = artists[1];
			}

			try
			{
				retrieveTopTrackTags(currentArtist);
			} catch (Exception e)
			{
				e.printStackTrace();
			}
		}
	}

}
